/************************************************************************
* history.c
* Copyright (C) Lisa Milne 2013 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "dish.h"
#include "env.h"

#include <ctype.h>

typedef struct history_s {
	struct history_s *prev;
	struct history_s *next;
	char* value;
} history_t;

static history_t *history = NULL;
static history_t *position = NULL;

/* load history from file */
void history_load()
{
	char buff[2048];
	int fd;
	char* h;
	char c;
	int r = 1;
	int o = 0;
	char s = 0;

	h = env_get("HOME");
	if (!h)
		return;

	sprintf(buff,"%s/.dish_history",h);
	fd = open(buff,O_RDONLY);
	if (fd < 0)
		return;

	while (r == 1) {
		r = read(fd,&c,1);
		if (r != 1)
			goto line_end;
		if (!isprint((int)c) && c != '\n')
			continue;
		if (c == s && (!o || buff[o-1] != '\\')) {
			s = 0;
		}else if (c == '\'' || c == '"') {
			s = c;
		}

		if (!s && c == '\n') {
line_end:
			buff[o] = 0;
			history_add(buff);
			o = 0;
			continue;
		}

		if (o < 2047)
			buff[o++] = c;
	}

	close(fd);
}

/* save history to file */
void history_save()
{
	char buff[1024];
	int fd;
	char* h;
	history_t *n;

	h = env_get("HOME");
	if (!h)
		return;

	sprintf(buff,"%s/.dish_history",h);
	fd = open(buff,O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
	if (fd < 0)
		return;

	n = history;
	while (n) {
		write(fd,n->value,strlen(n->value));
		write(fd,"\n",1);
		n = n->next;
	}
	close(fd);
}

/* add a command to history */
void history_add(char* str)
{
	history_t *h;
	history_t *p = history;
	if (!str || !str[0])
		return;

	if (history) {
		while (p->next) {
			p = p->next;
		}
		if (!strcmp(p->value,str)) {
			position = p;
			return;
		}
	}

	h = malloc(sizeof(history_t));
	h->prev = NULL;
	h->next = NULL;
	h->value = strdup(str);

	if (p) {
		p->next = h;
		h->prev = p;
	}else{
		history = h;
	}

	position = h;
}

/* get the previous command in history */
char* history_prev()
{
	history_t *p = position;
	if (!p)
		return "";

	if (p->prev)
		position = p->prev;
	return p->value;
}

/* get the next command in history */
char* history_next()
{
	if (!position || !position->next)
		return "";
	position = position->next;

	return position->value;
}
